Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: migrate api and core to ESM #733

Merged
merged 28 commits into from
Oct 11, 2023
Merged

feat: migrate api and core to ESM #733

merged 28 commits into from
Oct 11, 2023

Conversation

kanadgupta
Copy link
Member

@kanadgupta kanadgupta commented Sep 20, 2023

🚥 Resolves RM-7989

🧰 Changes

This very much (in-progress) PR migrates the api and core subpackages over to ESM. Since we're theoretically going to be using a build tool like Rollup or tsup to generate the ESM- and CJS-flavored SDKs, we theoretically should be able to move everything here to pure ESM (but could be wrong about that, will report back 😮‍💨)

Known Issues

🧬 QA & Testing

Provide as much information as you can on how to test what you've done.

@kanadgupta kanadgupta mentioned this pull request Sep 21, 2023
3 tasks
@kanadgupta kanadgupta mentioned this pull request Sep 29, 2023
3 tasks
@kanadgupta kanadgupta mentioned this pull request Oct 9, 2023
7 tasks
@erunion erunion added enhancement New feature or request refactor Issues about tackling technical debt labels Oct 10, 2023
@erunion erunion added area:core Issues related to `core`, which is the package that powers the SDKs at runtime area:snippets Issues related to code snippets area:api Issues related to the `api` CLI, which builds the SDKs labels Oct 10, 2023
@@ -2,11 +2,13 @@
"name": "api",
"version": "7.0.0-alpha.3",
"description": "Magical SDK generation from an OpenAPI definition 🪄",
"type": "module",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

api is now ESM-only. Codegen'd SDKs will support ESM and CJS but because api is a CLI-only tool we don't need to accommodate both module flows.

@@ -197,8 +196,8 @@ describe('typescript', () => {
});
});

it('should be able to make an API request (JS + CommonJS)', async () => {
const sdk = await import('../../__fixtures__/sdk/simple-js-cjs').then(r => r.default);
it.skip('should be able to make an API request (JS + CommonJS)', async () => {
Copy link
Member

@erunion erunion Oct 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests that are testing CJS built SDKs no longer work because of the way that these modules are handled now. These tests will get reworked in #734 when we refactor the codegen process to properly build CJS SDKs.

Comment on lines +1 to +2
// eslint-disable-next-line import/extensions
import '@api/test-utils/vitest.matchers.ts';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks funky but because the way the test-utils package is setup if we don't have this .ts extension here then the import for these Vitest matchers don't get properly loaded. @kanadgupta and I went through a whole pairing on session on this and settled on a method of getting it working with a very complicated exports object in the package.json for that package but that ultimately broke a lot of things downstream where we're loading definitions out of @api/test-utils/definitions.

Just adding this .ts extension, and the very minor change to the Vitest config to use ../test-utils instead of @api/test-utils is a cleaner option than a flaky package config.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so weird my goodness

"require": "./dist/index.cjs",
"import": "./dist/index.js"
},
"./errors/fetchError": {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the only other export because it might be nice for folks to be able to do instanceof FetchError in their implementations.

@@ -35,8 +31,6 @@ type Enumerate<N extends number, Acc extends number[] = []> = Acc['length'] exte

export type HTTPMethodRange<F extends number, T extends number> = Exclude<Enumerate<T>, Enumerate<F>>;

export { getJSONSchemaDefaults, parseResponse, prepareAuth, prepareParams, prepareServer };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not only are these utilities causing issues with the CJS export of this library but also these utilities are never used outside of core internals so they don't need to be exported.

@@ -35,8 +31,6 @@ type Enumerate<N extends number, Acc extends number[] = []> = Acc['length'] exte

export type HTTPMethodRange<F extends number, T extends number> = Exclude<Enumerate<T>, Enumerate<F>>;

export { getJSONSchemaDefaults, parseResponse, prepareAuth, prepareParams, prepareServer };

export default class APICore {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This default export alongside the named TS type exports is still causing issues with attw:

> @readme/[email protected] attw
> attw --pack --format table-flipped


@readme/api-core v7.0.0-alpha.3

Build tools:
- typescript@^5.2.2

❓ The JavaScript appears to set both module.exports and module.exports.default for improved compatibility, but the types only reflect the latter (by using export default). This will cause TypeScript under the node16 module mode to think an extra .default property access is required, which will work at runtime but is not necessary. These types export = an object with a default property instead of using export default. https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/MissingExportEquals.md

💀 Import failed to resolve to type declarations or JavaScript files. https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/NoResolution.md


┌──────────────────────────────────────┬───────────────────────┬───────────────────────┬───────────────────┬───────────┐
│                                      │ node10                │ node16 (from CJS)     │ node16 (from ESM) │ bundler   │
├──────────────────────────────────────┼───────────────────────┼───────────────────────┼───────────────────┼───────────┤
│ "@readme/api-core"                   │ ❓ Missing `export =` │ ❓ Missing `export =` │ 🟢 (ESM)          │ 🟢        │
├──────────────────────────────────────┼───────────────────────┼───────────────────────┼───────────────────┼───────────┤
│ "@readme/api-core/errors/fetchError" │ 💀 Resolution failed  │ 🟢 (CJS)              │ 🟢 (ESM)          │ 🟢        │
├──────────────────────────────────────┼───────────────────────┼───────────────────────┼───────────────────┼───────────┤
│ "@readme/api-core/package.json"      │ 🟢 (JSON)             │ 🟢 (JSON)             │ 🟢 (JSON)         │ 🟢 (JSON) │
└──────────────────────────────────────┴───────────────────────┴───────────────────────┴───────────────────┴───────────┘

Everything seems to work with the dist but it still worries me. I attempted to refactor the TS type exports out into a types.ts file, which satisfies attw but using that types file is another story:

diff --git a/packages/core/package.json b/packages/core/package.json
index 5d16e2b..4e95761 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -13,6 +13,10 @@
       "require": "./dist/errors/fetchError.cjs",
       "import": "./dist/errors/fetchError.js"
     },
+    "./types": {
+      "require": "./dist/types.d.cts",
+      "import": "./dist/types.d.ts"
+    },
     "./package.json": "./package.json"
   },
   "main": "dist/index.cjs",

Screen Shot 2023-10-09 at 5 37 16 PM

@@ -74,7 +74,7 @@ function getAuthSources(operation: Operation) {
return matchers;
}

export interface APIOptions {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This interface is never used outside of the library so it doesn't need to be exported.

@@ -87,6 +87,7 @@ const client: Client<APIOptions> = {
title: 'API',
link: 'https://npm.im/api',
description: 'Automatic SDK generation from an OpenAPI definition.',
extname: '.js',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a new requirement I added in @readme/httpsnippet when I upgraded it to support ESM.

@@ -111,7 +110,7 @@ describe('httpsnippet-client-api', () => {
it('should generate the expected snippet', async () => {
const expected = await fs.readFile(path.join(DATASETS_DIR, snippet, 'output.js'), 'utf-8');

const code = new HTTPSnippet(mock.har).convert('node', 'api', {
const code = await new HTTPSnippet(mock.har).convert('node', 'api', {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks to some native fetch improvements I did in @readme/httpsnippet, completely dropping support for funky FormData objects that node-fetch@2 supports, the convert function there is now async.

@erunion erunion marked this pull request as ready for review October 10, 2023 00:44
Copy link
Member Author

@kanadgupta kanadgupta left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changes look good but I don't think we need tsup for the api and core packages, more on this below!

packages/api/tsup.config.ts Outdated Show resolved Hide resolved
Copy link
Member Author

@kanadgupta kanadgupta Oct 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay i also don't think we need to build this subpackage with tsup either and I think we can just rely on tsc — I was able to get pretty far in #734 with just the ESM build of this subpackage! i say this for several reasons:

  1. to eliminate the confusing accommodations we have to make here (see this comment)

  2. we're using tsup at the code-gen level in feat: esm + tsup #734, which will take care of bundling the ESM code and spitting out proper CJS/ESM SDKs. this pretty much eliminates the need for us to do any dual-export mess for downstream packages (with the exception of packages that we need for cjs repos like our main app, of course)

Copy link
Member

@erunion erunion Oct 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think? that if we dont offer cjs exports of this library then cjs codegend sdks wont be able to use it because itll be esm only and tsup wont touch it because its loaded in code as a dependency

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ill change this and see how it shakes out

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

slept on it for a few days and i'm a bit nervous about making core ESM-only with tsc so im going to wait until the tsup codegen work is done before making any refactors to the core build process.

tsconfig.base.json Outdated Show resolved Hide resolved
@erunion erunion merged commit f65bc79 into main Oct 11, 2023
5 checks passed
@erunion erunion deleted the esm branch October 11, 2023 16:14
@erunion erunion added this to the v7 milestone Oct 19, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:api Issues related to the `api` CLI, which builds the SDKs area:core Issues related to `core`, which is the package that powers the SDKs at runtime area:snippets Issues related to code snippets enhancement New feature or request refactor Issues about tackling technical debt
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants